
import { toRaw } from 'vue'

import {
  copy 
} from '../base/assign'

// 日志
import {
  stateLog, // 记录容器
  writeLog // 添加一条记录
} from '../base/_log'

import type {
  IState,
  IStateKey,
  IAnyObject
} from '../types/type'

/**
 * 给对象加上辅助功能：$state、$patch、$reset
 * @param objOrFunction 初始值，可以是对象，也可以是函数
 * @param id 标识，记录日志用
 * @param isLog 是否记录日志
 */
export default class BaseObject<T extends IAnyObject> implements IState {
  #id: IStateKey
  #isLog: boolean
  // #isModel = true
  // #isObject = true
  // #isDeep: boolean // 如果属性类型是对象、数组，是否转换
  #_value: T | (() => T) // 初始值或者初始函数 IObjectOrFunction

  /**
   * 创建一个基础的对象，实现辅助工具的功能
   * @param objOrFunction 初始值，可以是对象，也可以是函数
   * @param id 标识，记录日志用
   * @param isLog 是否记录日志
   */
  constructor (
    objOrFunction: T | (() => T),
    id: IStateKey = Symbol('_object'),
    isLog = false
  ) {
    // this.#isDeep = isDeep
    this.#id = id
    this.#isLog = isLog
    switch (typeof objOrFunction) {
      case 'function':
        // 记录初始函数
        this.#_value = objOrFunction
        // 执行函数获得对象，设置具体的属性，浅层拷贝
        Object.assign(this, (objOrFunction as (() => T))())
        break
      case 'object':
        // 记录初始值的副本，使用 json 拷贝，对象形式的初始值
        this.#_value = JSON.parse(JSON.stringify(objOrFunction)) // Object.assign(objOrFunction)
        // 设置具体的属性，浅层拷贝
        Object.assign(this, objOrFunction)
        break
      default:
        // 不支持其他类型
        this.#_value = {} as T | (() => T)
        break
    }
    
  }

  // 操作状态的函数
  /**
   * 获取初始值，如果是函数的话，会调用函数返回结果
   */
  get $value() {
    const val = toRaw(this).#_value
    const re = typeof val === 'function' ? val() : val
    return re
  }

  /**
   * 设置初始值
   */
  set $value(val: T | (() => T)) {
    toRaw(this).#_value = val
  }

  /**
   * 恢复初始值，值支持单层
   */
  $reset() {
    // 模板里面触发的事件，没有 this
    if (this) {
      writeLog(this, this.$value, '$reset', 3, () => {
        copy(this, this.$value)
        // Object.assign(this, this.$value)
      })
    } else {
      console.warn('在 template 里面使用的时候，请加上()，比如： foo.$reset()。')
    }
  }

  /**
   * 设置新值
   */
  set $state(value: T) {
    // 要不要判断 value 的属性是否完整？
    writeLog(this, value, '$state', 5, () => {
      copy(this, value)
      // Object.assign(this, value)
    })
  }

  /**
   * 替换部分属性，只支持单层
   */
  async $patch(obj: IAnyObject | ((val: T) => void)) {
    writeLog(this, obj, '$patch', 3, async () => {
      if (typeof obj === 'function') {
        // 回调，不接收返回值
        await obj(this)
      } else {
        // 赋值
        copy(this, obj)
        // Object.assign(this, obj)
      }
    })
  }
  
  /**
   * 取原型，不包含内部方法
  */
  $toRaw<T extends IAnyObject>(): T {
    const obj: IAnyObject = {} as IAnyObject
    const tmp: IAnyObject = toRaw(this)
    Object.keys(tmp).forEach((key: IStateKey) => {
    // for (const key in tmp) {
      if (typeof key === 'symbol') {
        obj[key] = (tmp[key].$toRaw) ? tmp[key].$toRaw() : toRaw(tmp[key])
      } else {
        if ((key as string).substring(0,1) !== '#') {
          obj[key] = (tmp[key].$toRaw) ? tmp[key].$toRaw() : toRaw(tmp[key])
        }
      }
    })
    return obj as T
  }
  
  // 各种标识
  /**
   * 获取标识，string | symbol
   */
  get $id(): IStateKey {
    return toRaw(this).#id
  }
  /**
   * 获取是否记录日志。true ：记日志；false： 不记日志（默认值）
   */
  get $isLog() {
    return toRaw(this).#isLog
  }
  /**
   * 设置是否写日志
   */
  set $isLog(val: boolean) {
    toRaw(this).#isLog = val
  }
  /**
   * 验证是不是充血实体类的状态
   */
  get $isState() {
    return false
  }
  /**
   * 验证是不是有辅助工具，区分普通的对象
   */
  get $isObject() {
    return true
  }
  /**
   * 验证是不是有辅助工具，区分普通的数组
   */
  get $isArray() {
    return false
  }
  
  /**
   * 获取日志
   */
  get $logs() {
    if (stateLog[this.$id]) {
      return stateLog[this.$id].log
    } else {
      return []
    }
  }
  
  /**
   * 清空日志
   */
  $clearLog() {
    if (stateLog[this.$id]) {
      stateLog[this.$id].log.length = 0
    }
  }
}